<template>
  <CesiumBox ref="container"></CesiumBox>
</template>

<script setup>
import CesiumBox from "../../components/CesiumBox.vue";
import { ref, onMounted } from "vue";
import * as Cesium from "cesium";

const container = ref(null);
let cesiumPara = null;

function addPolygon() {
  Cesium.Material._materialCache.addMaterial("CustomWater", {
    fabric: {
      type: "CustomWater",
      uniforms: {
        baseWaterColor: new Cesium.Color(
          64.0 / 255.0,
          157.0 / 255.0,
          253.0 / 255.0,
          0.6
        ),
        blendColor: new Cesium.Color(0.0, 1.0, 0.699, 1.0),
        normalMap: "./three/textures/waternormals.jpg",
        specularMap: Cesium.Material.DefaultImageId,
        frequency: 100.0,
        animationSpeed: 0.01,
        amplitude: 20,
        specularIntensity: 5,
        fadeFactor: 1.0,
      },
      source: `
          uniform vec4 baseWaterColor;
          uniform vec4 blendColor;
          uniform sampler2D specularMap;
          uniform sampler2D normalMap;
          uniform float frequency;
          uniform float animationSpeed;
          uniform float amplitude;
          uniform float specularIntensity;
          uniform float fadeFactor;
  
          czm_material czm_getMaterial(czm_materialInput materialInput)
          {
            czm_material material = czm_getDefaultMaterial(materialInput);
        
            float time = czm_frameNumber * animationSpeed;
        
            // fade is a function of the distance from the fragment and the frequency of the waves
            float fade = max(1.0, (length(materialInput.positionToEyeEC) / 10000000000.0) * frequency * fadeFactor);
        
            float specularMapValue = texture2D(specularMap, materialInput.st).r;
        
            // note: not using directional motion at this time, just set the angle to 0.0;
            vec4 noise = czm_getWaterNoise(normalMap, materialInput.st * frequency, time, 0.0);
            vec3 normalTangentSpace = noise.xyz * vec3(1.0, 1.0, (1.0 / amplitude));
        
            // fade out the normal perturbation as we move further from the water surface
            normalTangentSpace.xy /= fade;
        
            // attempt to fade out the normal perturbation as we approach non water areas (low specular map value)
            normalTangentSpace = mix(vec3(0.0, 0.0, 50.0), normalTangentSpace, specularMapValue);
        
            normalTangentSpace = normalize(normalTangentSpace);
        
            // get ratios for alignment of the new normal vector with a vector perpendicular to the tangent plane
            float tsPerturbationRatio = clamp(dot(normalTangentSpace, vec3(0.0, 0.0, 1.0)), 0.0, 1.0);
        
            // fade out water effect as specular map value decreases
            material.alpha = mix(blendColor.a, baseWaterColor.a, specularMapValue) * specularMapValue;
        
            // base color is a blend of the water and non-water color based on the value from the specular map
            // may need a uniform blend factor to better control this
            material.diffuse = mix(blendColor.rgb, baseWaterColor.rgb, specularMapValue);
        
            // diffuse highlights are based on how perturbed the normal is
            material.diffuse += (0.1 * tsPerturbationRatio);
        
            material.diffuse = material.diffuse;
        
            material.normal = normalize(materialInput.tangentToEyeMatrix * normalTangentSpace);
        
            material.specular = specularIntensity;
            material.shininess = 30.0;
        
            return material;
          }
        `,
    },
    translucent: function (material) {
      const uniforms = material.uniforms;
      return (
        uniforms.baseWaterColor.alpha < 1.0 || uniforms.blendColor.alpha < 1.0
      );
    },
  });

  let appearance = new Cesium.EllipsoidSurfaceAppearance({
    material: new Cesium.Material({
      fabric: {
        type: "CustomWater",
      },
    }),
    materialSupport: Cesium.MaterialAppearance.MaterialSupport.TEXTURED,
  });

  let primitiveCustom = new Cesium.Primitive({
    geometryInstances: new Cesium.GeometryInstance({
      geometry: new Cesium.PolygonGeometry({
        polygonHierarchy: new Cesium.PolygonHierarchy(
          Cesium.Cartesian3.fromDegreesArray([
            113, 43, 112, 44, 113, 45, 115, 45, 116, 44, 115, 43,
          ])
        ),
        height: 5000,
        vertexFormat:
          Cesium.MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat,
      }),
    }),
    appearance: appearance,
    asynchronous: false,
  });
  cesiumPara.viewer.scene.primitives.add(primitiveCustom);

  let primitiveCesium = new Cesium.Primitive({
    geometryInstances: new Cesium.GeometryInstance({
      geometry: new Cesium.PolygonGeometry({
        polygonHierarchy: new Cesium.PolygonHierarchy(
          Cesium.Cartesian3.fromDegreesArray([
            116, 46, 115, 47, 116, 48, 118, 48, 119, 47, 118, 46,
          ])
        ),
        height: 2000,
        vertexFormat:
          Cesium.MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat,
      }),
    }),
    appearance: new Cesium.EllipsoidSurfaceAppearance({
      material: new Cesium.Material({
        fabric: {
          type: "Water",
          uniforms: {
            baseWaterColor: new Cesium.Color(
              64 / 255.0,
              157 / 255.0,
              253 / 255.0,
              0.5
            ),
            normalMap: "./three/textures/waternormals.jpg",
            frequency: 1000.0,
            animationSpeed: 0.1,
            amplitude: 10,
            specularIntensity: 10,
          },
        },
      }),
    }),
    asynchronous: false,
  });
  cesiumPara.viewer.scene.primitives.add(primitiveCesium);
}

onMounted(() => {
  cesiumPara = container.value.cesiumPara;
  addPolygon();

  cesiumPara.viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(
      125.59719837773976,
      34.511151215544444,
      1438496.33035057
    ),
    orientation: {
      heading: 5.900144937823217,
      pitch: -0.8260025319357904,
      roll: 6.282441569785731,
    },
    duration: 3,
  });
});
</script>
